/**
 Copyright (c) 2015-present, Facebook, Inc.
 All rights reserved.

 This source code is licensed under the BSD-style license found in the
 LICENSE file in the root directory of this source tree.
 */

#include <gtest/gtest.h>
#include <graphics/Format/PNG.h>
#include <graphics/Image.h>
#include <graphics/PixelFormat.h>

using graphics::Format::PNG;
using graphics::Image;
using graphics::PixelFormat;

/* Gray */
static void
Gray(std::vector<uint8_t> *png, std::vector<uint8_t> *pixels, PixelFormat *pixelFormat)
{
    *png = {
        0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
        0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
        0x08, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x7e, 0x9b, 0x55, 0x00, 0x00, 0x00,
        0x0a, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0xc8, 0x02, 0x00, 0x00,
        0x6c, 0x00, 0x6b, 0x9e, 0x0d, 0xf6, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x49,
        0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
    };

    *pixels = {
        0x6A,
    };

    *pixelFormat = PixelFormat(
        PixelFormat::Color::Grayscale,
        PixelFormat::Order::Forward,
        PixelFormat::Alpha::None);
}

/* Gray Alpha */
static void
GrayAlpha(std::vector<uint8_t> *png, std::vector<uint8_t> *pixels, PixelFormat *pixelFormat)
{
    *png = {
        0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
        0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
        0x08, 0x04, 0x00, 0x00, 0x00, 0xb5, 0x1c, 0x0c, 0x02, 0x00, 0x00, 0x00,
        0x0b, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0xc8, 0xf2, 0x03, 0x00,
        0x01, 0x25, 0x00, 0xb9, 0x7e, 0x00, 0xb5, 0x68, 0x00, 0x00, 0x00, 0x00,
        0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
    };

    *pixels = {
        0x6A, 0x4E,
    };

    *pixelFormat = PixelFormat(
        PixelFormat::Color::Grayscale,
        PixelFormat::Order::Forward,
        PixelFormat::Alpha::Last);
}

/* Color */
static void
Color(std::vector<uint8_t> *png, std::vector<uint8_t> *pixels, PixelFormat *pixelFormat)
{
    *png = {
        0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
        0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
        0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xde, 0x00, 0x00, 0x00,
        0x0c, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0xf8, 0xff, 0x9f, 0x01,
        0x00, 0x04, 0xff, 0x01, 0xff, 0x5d, 0x7d, 0x02, 0x87, 0x00, 0x00, 0x00,
        0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
    };

    *pixels = {
        0xFF, 0xFF, 0x00,
    };

    *pixelFormat = PixelFormat(
        PixelFormat::Color::RGB,
        PixelFormat::Order::Forward,
        PixelFormat::Alpha::None);
}

/* Color Alpha */
static void
ColorAlpha(std::vector<uint8_t> *png, std::vector<uint8_t> *pixels, PixelFormat *pixelFormat)
{
    *png = {
        0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
        0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
        0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0x15, 0xc4, 0x89, 0x00, 0x00, 0x00,
        0x0d, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0x63, 0xf8, 0xff, 0x9f, 0x21,
        0x0b, 0x00, 0x07, 0x68, 0x02, 0x69, 0x60, 0xd1, 0x63, 0x78, 0x00, 0x00,
        0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
    };

    *pixels = {
        0xFF, 0xFF, 0x00, 0x6A,
    };

    *pixelFormat = PixelFormat(
        PixelFormat::Color::RGB,
        PixelFormat::Order::Forward,
        PixelFormat::Alpha::Last);
}

/* All Tests */
static void (*PNGTests[])(std::vector<uint8_t> *, std::vector<uint8_t> *, PixelFormat *) = {
    Gray,
    GrayAlpha,
    Color,
    ColorAlpha,
};

TEST(PNG, Read)
{
    for (size_t i = 0; i < sizeof(PNGTests) / sizeof(*PNGTests); i++) {
        /* Load test data. */
        auto const &test = PNGTests[i];
        std::vector<uint8_t> png;
        std::vector<uint8_t> pixels;
        PixelFormat format = PixelFormat(PixelFormat::Color::Grayscale, PixelFormat::Order::Forward, PixelFormat::Alpha::None);
        test(&png, &pixels, &format);

        /* Should be able to read PNG. */
        auto result = PNG::Read(png);
        ASSERT_NE(result.first, ext::nullopt);
        Image const &image = *result.first;

        /* Should have expected pixels in specified format. */
        std::vector<uint8_t> converted = PixelFormat::Convert(image.data(), image.format(), format);
        EXPECT_EQ(converted, pixels);
    }
}

TEST(PNG, Write)
{
    for (size_t i = 0; i < sizeof(PNGTests) / sizeof(*PNGTests); i++) {
        /* Load test data. */
        auto const &test = PNGTests[i];
        std::vector<uint8_t> png;
        std::vector<uint8_t> pixels;
        PixelFormat format = PixelFormat(PixelFormat::Color::Grayscale, PixelFormat::Order::Forward, PixelFormat::Alpha::None);
        test(&png, &pixels, &format);

        /* Should be able to write PNG. */
        auto image = Image(1, 1, format, pixels);
        auto result = PNG::Write(image);
        ASSERT_NE(result.first, ext::nullopt);

        /* Should have expected PNG data. */
        EXPECT_EQ(*result.first, png);
    }
}
